#include "qwt3d_drawable.h"

using namespace Qwt3D;

Drawable::~Drawable()
{
	detachAll();
}

void Drawable::saveGLState()
{
	glGetBooleanv(GL_LINE_SMOOTH, &ls);
	glGetBooleanv(GL_POLYGON_SMOOTH, &pols);
	glGetFloatv(GL_LINE_WIDTH, &lw);
	glGetIntegerv(GL_BLEND_SRC, &blsrc);
	glGetIntegerv(GL_BLEND_DST, &bldst);
	glGetDoublev(GL_CURRENT_COLOR, col);
	glGetIntegerv(GL_LINE_STIPPLE_PATTERN, &pattern);
	glGetIntegerv(GL_LINE_STIPPLE_REPEAT, &factor);
	glGetBooleanv(GL_LINE_STIPPLE, &sallowed);
	glGetBooleanv(GL_TEXTURE_2D, &tex2d);
	glGetIntegerv(GL_POLYGON_MODE, polmode);
	glGetIntegerv(GL_MATRIX_MODE, &matrixmode);
	glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &poloffs[0]);
	glGetFloatv(GL_POLYGON_OFFSET_UNITS, &poloffs[1]);
	glGetBooleanv(GL_POLYGON_OFFSET_FILL, &poloffsfill);
}

void Drawable::restoreGLState()
{
	Enable(GL_LINE_SMOOTH, ls);
	Enable(GL_POLYGON_SMOOTH, pols);

	setLineWidth(lw);
	glBlendFunc(blsrc, bldst);
	glColor4dv(col);

	glLineStipple(factor,pattern);
	Enable(GL_LINE_STIPPLE,sallowed);
	Enable(GL_TEXTURE_2D,tex2d);
	glPolygonMode(polmode[0], polmode[1]);
	glMatrixMode(matrixmode);
	glPolygonOffset(poloffs[0], poloffs[1]);
	glPolygonOffset(poloffs[0], poloffs[1]);

	Enable(GL_POLYGON_OFFSET_FILL, poloffsfill);
}

void Drawable::Enable(GLenum what, GLboolean val)
{
	if (val)
		glEnable(what);
	else
		glDisable(what);
}

void Drawable::attach(Drawable* dr)
{
	if ( dlist.end() == std::find( dlist.begin(), dlist.end(), dr ) )
		if (dr)
		{
			dlist.push_back(dr);
		}
}

void Drawable::detach(Drawable* dr)
{
	std::list<Drawable*>::iterator it = std::find(dlist.begin(), dlist.end(), dr);

	if ( it != dlist.end() )
	{
		dlist.erase(it);
	}
}

void Drawable::detachAll()
{
	dlist.clear();
}


//! simplified glut routine (glUnProject): windows coordinates_p --> object coordinates_p
/**
	Don't rely on (use) this in display lists !
*/
Triple Drawable::ViewPort2World(Triple win, bool* err)
{
	Triple obj;

	getMatrices(modelMatrix, projMatrix, viewport);
	int res = gluUnProject(win.x, win.y, win.z, modelMatrix, projMatrix, viewport, &obj.x, &obj.y, &obj.z);

	if (err)
		*err = (res) ? false : true;
	return obj;
}

//! simplified glut routine (glProject): object coordinates_p --> windows coordinates_p
/**
	Don't rely on (use) this in display lists !
*/
Triple Drawable::World2ViewPort(Triple obj,	bool* err)
{
	Triple win;

	getMatrices(modelMatrix, projMatrix, viewport);
	int res = gluProject(obj.x, obj.y, obj.z, modelMatrix, projMatrix, viewport, &win.x, &win.y, &win.z);

	if (err)
		*err = (res) ? false : true;
	return win;
}

/**
	Don't rely on (use) this in display lists !
*/
Triple Drawable::relativePosition(Triple rel)
{
	return ViewPort2World(Triple((rel.x-viewport[0])*viewport[2],(rel.y-viewport[1])*viewport[3],rel.z));
}

void Drawable::draw()
{
	saveGLState();

	for (std::list<Drawable*>::iterator it = dlist.begin(); it!=dlist.end(); ++it)
	{
		(*it)->draw();
	}

	restoreGLState();
}

void Drawable::setColor(double r, double g, double b, double a)
{
	color = RGBA(r,g,b,a);
}

void Drawable::setColor(RGBA rgba)
{
	color = rgba;
}
